#include "gameroom.h"
#include <QDebug>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QTcpSocket>

void writeSocket(QTcpSocket *socket, const QByteArray &data) {
  if (socket == nullptr || !socket->isValid()) {
    qDebug() << __func__ << __LINE__ << "socket is nullptr";
    return;
  }
  socket->write(data + ";");
  socket->flush();
}

void writeSocket(QTcpSocket *socket, const QJsonObject &data) {
  if (socket == nullptr || !socket->isValid()) {
    qDebug() << __func__ << __LINE__ << "socket is nullptr";
    return;
  }
  QJsonDocument doc(data);
  writeSocket(socket, doc.toJson(QJsonDocument::Compact));
}

GameRoom::GameRoom(const QString &roomName, QObject *parent)
    : QObject(parent), m_roomName(roomName) {}

const QString &GameRoom::roomName() { return m_roomName; }

bool GameRoom::exist(const QString &nick) {
  for (User *user : users) {
    if (user->nick == nick)
      return true;
  }
  return false;
}

bool GameRoom::join(User *user) {
  users.append(user);
  return true;
}

bool GameRoom::leave(User *user) {
  users.removeOne(user);
  notify("leave", user->nick);
  return true;
}

void GameRoom::notify(const QString &cmd, const QString &msg) {
  for (User *user : this->users) {
    notifyOne(user, cmd, msg);
  }
}

void GameRoom::notifyOne(User *user, const QString &cmd, const QString &msg) {
  if (user->sock == nullptr) {
    qDebug() << __func__ << __LINE__ << user->nick;
    return;
  }
  QJsonObject obj;
  obj["type"] = "notify";
  obj["event"] = cmd;
  obj["msg"] = msg;
  writeSocket(user->sock, obj);
}

void GameRoom::say(int idx) {
  if (idx >= 0)
    sayIdx = 0;
  int size = users.size();
  for (; sayIdx < size; ++sayIdx) {
    User *user = users.at(sayIdx);
    if (!user->out) {
      notifyOne(user, "talk");
      break;
    }
  }
  ++sayIdx;

  // 判断是否为最后一个人发言,是发送投票
  if (sayIdx > size) {
    syncNotify();
    notify("ticket");
    sayIdx = 0;
  }
}

void GameRoom::vote(const QString &nick, const QString &other) {
  muxVote.lock();
  votes.append(QPair<QString, QString>(nick, other));
  int size = votes.size();
  muxVote.unlock();
  int online = 0;
  for (User *user : users) {
    if (!user->out)
      ++online;
  }
  if (size < online)
    return;

  // 投票结束
  // 统计谁out了
  QMap<QString, int> satai;
  for (const auto &pair : votes) {
    if (!satai.contains(pair.second)) {
      satai[pair.second] = 0;
    }
    satai[pair.second]++;
  }
  muxVote.lock();
  votes.clear();
  muxVote.unlock();

  QString info = "Vote Result:";
  QString outNick;
  int max = 0;
  for (auto i = satai.begin(); i != satai.end(); ++i) {
    info.append(QString("%1:%2 ").arg(i.key()).arg(i.value()));
    if (outNick.isEmpty()) {
      max = i.value();
      outNick = i.key();
    }
    if (max < i.value()) {
      max = i.value();
      outNick = i.key();
    }
  }
  if (satai.size() > 0)
    notify("say", info);

  for (User *user : users) {
    if (user->nick == outNick) {
      syncNotify();
      user->out = true;
      --online;
    }
  }

  //游戏结束
  if (online <= this->last) {
    bool under = false;
    for (User *user : users) {
      if (!user->out && undercoverKey == user->word) {
        under = true;
        break;
      }
    }
    notify("result", under ? undercoverKey : plebeian);
    return;
  }
  say(0);
}

void GameRoom::syncNotify() {
  QJsonObject data = toJsonObject();
  for (User *user : users) {
    writeSocket(user->sock, data);
  }
}

User *GameRoom::user(const QString &nick) {
  for (User *user : users)
    if (user->nick == nick)
      return user;
  return nullptr;
}

QJsonObject GameRoom::toJsonObject() {
  QJsonArray array;
  for (User *user : users) {
    QJsonObject obj;
    obj["name"] = user->nick;
    obj["master"] = user->created;
    obj["word"] = user->word;
    obj["out"] = user->out;
    array.append(obj);
  }

  QJsonObject obj;
  obj["type"] = "sync";
  obj["room"] = m_roomName;
  obj["users"] = array;
  obj["play"] = palyed;
  return obj;
}

void GameRoom::allocWord() {
  QStringList words = m_words.split(";");
  int wordat = std::rand() % words.size();
  QString pair = words.at(wordat);
  QStringList wordList = pair.split(",");
  if (wordList.size() != 2) {
    qDebug() << wordat << __LINE__ << __FUNCTION__ << pair;
    return;
  }
  QString undercover = wordList.at(0);
  QString temp = wordList.at(1);
  for (User *user : this->users) {
    user->word = temp;
  }
  undercoverKey = undercover;
  plebeian = temp;
  for (int i = 0, pos = std::rand() % users.size(); i < this->m_undercover;
       pos++) {
    int idx = pos % users.size();
    if (users.at(idx)->word == plebeian) {
      users.at(idx)->word = undercover;
      ++i;
    }
  }
  this->palyed = true;
  syncNotify();
}
